package com.hxzy.common.aspect;

import cn.hutool.core.net.Ipv4Util;
import com.hxzy.common.annotates.RateLimiter;
import com.hxzy.common.annotates.RequiredRole;
import com.hxzy.common.enums.AckCode;
import com.hxzy.common.enums.LimitType;
import com.hxzy.common.exception.ServiceException;
import com.hxzy.common.exception.ServiceStringException;
import com.hxzy.common.util.IpUtils;
import com.hxzy.common.util.MarketThreadLocalUtil;
import com.hxzy.controller.admin.login.resp.AdminLoginResp;
import lombok.extern.log4j.Log4j2;
import org.aspectj.lang.JoinPoint;
import org.aspectj.lang.annotation.Aspect;
import org.aspectj.lang.annotation.Before;
import org.aspectj.lang.annotation.Pointcut;
import org.springframework.beans.factory.annotation.Autowired;
import org.springframework.data.redis.core.RedisTemplate;
import org.springframework.data.redis.core.script.RedisScript;
import org.springframework.stereotype.Component;

import java.util.Arrays;
import java.util.Objects;

/**
 * 限流的AOP
 */
@Log4j2
@Component
@Aspect
public class RateLimiterAspect {

    /**
     * 注解限流执行器
     */
    @Autowired
    private RedisScript<Long>  limitScript;

    @Autowired
    private RedisTemplate redisTemplate;

    //1、定义切入点（查询哪些类中的哪些方法），5种增强接口 @Before  @After  @AfterThrowing  @AfterReturing @Around
    @Pointcut("@annotation(com.hxzy.common.annotates.RateLimiter)")
    private void rateLimiterExpression() {
    }

    /**
     * 定义增强的功能
     */
    @Before(value = "rateLimiterExpression() && @annotation(rateLimiter)")
    public void beforeCheckRole(JoinPoint joinPoint, RateLimiter rateLimiter) {
        int time = rateLimiter.time();
        int count = rateLimiter.count();

        //组装redisKey
        String methodName=joinPoint.getSignature().getName();
        //rate_limit:方法名(这个方法限流)
        String redisKey= rateLimiter.key()+methodName;
        //IP限流（个人限流）
        if(rateLimiter.limitType()== LimitType.IP){
           String ip = IpUtils.getIpAddr();
           redisKey+=":"+ip;
        }

        try {
            Long result = (Long) this.redisTemplate.execute(this.limitScript, Arrays.asList(redisKey), count, time);
            if(Objects.isNull(result) || result.intValue() > count){
                throw new ServiceException(AckCode.RATE_LIMATE);
            }

        }catch(ServiceException e){
            throw e;
        }catch(Exception e){
            log.error(e);
            throw new ServiceStringException("redis限流脚本执行失败");
        }
    }


}
